package pho.prowon.category;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;

import pho.helper.DateConverter;
import pho.prowon.ImageDataContainer;

/**
 * @author Peter
 *
 * The category that actually contains images, but can also contain further
 * subcategories
 */
public class VisibleIndexCategory extends IndexCategory 
{
	/**
	 * @author Peter
	 *
	 * this is a comparator to sort images contained correctly...
	 */
	private class ImageContainerComparator implements Comparator
	{
		private ArrayList sortCriteria;
		private boolean ascending = true;

		public ImageContainerComparator ()
		{
			this (true);
		}
		
		public ImageContainerComparator (boolean asc)
		{
			sortCriteria = null;
			ascending = asc;
		}

		public ImageContainerComparator (List sortFields)
		{
			this (sortFields, true);
		}
		
		public ImageContainerComparator (List sortFields, boolean asc)
		{			
			sortCriteria = new ArrayList (sortFields);
			ascending = asc;
		}

		public ImageContainerComparator (String sortField)
		{
			this(sortField,true);
		}
		
		public ImageContainerComparator (String sortField, boolean asc)
		{
			sortCriteria = new ArrayList ();
			sortCriteria.add (sortField);
			ascending = asc;
		}

		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		public int compare(Object o1, Object o2)
		{
			if (sortCriteria == null)
			{				
				return ((ImageDataContainer)o1).toString().compareTo (((ImageDataContainer)o2).toString()) * (ascending ? 1 : -1);
			}
			else
			{
				Iterator i = sortCriteria.iterator();
				while (i.hasNext())
				{
					String cmp = (String) i.next();
					int result = ((ImageDataContainer)o1).getImageDataTag(cmp).compareTo (((ImageDataContainer)o2).getImageDataTag(cmp));
					if (result != 0)
					{
						return result * (ascending ? 1 : -1);											
					}
				}
				return 0;
			}
		}
	}

	private String tag = "";
	private ArrayList sortedImages = new ArrayList ();
	private Comparator sorter = new ImageContainerComparator ();


	/**
	 * This is the constructor, it will only be called from a RootIndexCategory
	 * The tag is for the category to know after which tag it is categorized and
	 * the name is the value of the tag it contains the images for
	 * @param String theTag
	 * @param String theName
	 * @param IndexCategory theParent
	 */
	protected VisibleIndexCategory(String theTag, String theName, IndexCategory theParent)
	{
		super (theParent, theName);
		tag = theTag;
	}

	protected ImageDataContainer addImage(ImageDataContainer image, boolean resort)
	{
		if (!sortedImages.contains(image))
		{
			sortedImages.add( image);
			((Observable)image).addObserver(this);
			if (resort)
			{
				Collections.sort (sortedImages, sorter);
			}
		}
		return image;
	}

	// only removes the image from this category
	protected void removeImage(ImageDataContainer image)
	{
		sortedImages.remove( image);
		((Observable)image).deleteObserver(this);
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexCategory#getAllImages()
	 */
	public List getAllImages()
	{
		return getRoot().getAllImages();
	}

	/**
	 * This returns a list of images that are contained in this category (not in the
	 * subcategories)
	 * @return List of ImageDataContainers
	 */
	public List getImages()
	{
		return (List) sortedImages.clone();
	}


	/**
	 * Returns whether this category contains any images at all
	 * @return true if images are contained
	 */
	public boolean hasImages()
	{
		return !sortedImages.isEmpty();
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexCategory#getRoot()
	 */
	public RootIndexCategory getRoot()
	{
		return getParent().getRoot();
	}
	
	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexCategory#clear()
	 */
	public void clear ()
	{
		super.clear();
		
		Iterator catIt = getImages().iterator();
		while (catIt.hasNext())
		{
			ImageDataContainer idc = (ImageDataContainer) catIt.next();
			getRoot().removeImage(idc);
		}		
	}

	/**
	 * Resorts all the images using the default comparator recursively through all categories 
	 */
	public void resortAll ()
	{
		super.resortAll ();
		resort ();
	}

	/**
	 * Resorts the images using the default comparator 
	 */
	public void resort ()
	{
		resort (true);
	}

	/**
	 * Resorts the images using the default comparator 
	 */
	public void resort (boolean asc)
	{
		sorter = new ImageContainerComparator (asc);
		Collections.sort (sortedImages,sorter); 
	}
	
	/**
	 * resorts the images using the List of Tag names passed
	 * @param s List of Tag names for sort order
	 */
	public void resort (List s)
	{
		resort (s,true);
	}

	/**
	 * resorts the images using the List of Tag names passed
	 * @param s List of Tag names for sort order
	 */
	public void resort (List s, boolean asc)
	{
		sorter = new ImageContainerComparator (s, asc);
		Collections.sort (sortedImages,sorter); 
	}
	
	/**
	 * resorts the images using the Tag name passed
	 * @param s Tag name for sort order
	 */
	public void resort (String s)
	{
		resort (s,true);
	}

	/**
	 * resorts the images using the Tag name passed
	 * @param s Tag name for sort order
	 */
	public void resort (String s, boolean asc)
	{
		sorter = new ImageContainerComparator (s, asc);
		Collections.sort (sortedImages,sorter); 
	}

	/**
	 * refreshes the categorization of the image, remove it from the current
	 * and add it to its new category if its category has changed
	 * @param image_container the image to be refreshed
	 */
	public void refreshImage(ImageDataContainer image_container)
	{
		if (getImages().contains(image_container))
		{
			if (getRoot().hasCategoryChanged(this,image_container))
			{
				removeImage(image_container);					
				getRoot().categorizeImage(image_container,true);
				getParent().refresh ();
			}
		}
	}


	/**
	 * remove all images from this category and recursively its subcategories
	 */
	public void removeAll ()
	{
		Iterator ii = getImages().iterator();
		while (ii.hasNext())
		{
			getRoot().removeImage((ImageDataContainer)ii.next());
		}

		ii = getCategories().iterator();
		while (ii.hasNext())
		{
			VisibleIndexCategory ic = (VisibleIndexCategory) ii.next(); 
			ic.removeAll();
		}
		getParent().refresh ();
	}

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexCategory#rename(java.lang.String)
	 */
	// this is currently only safe for single tags, not arrays	
	public void rename (String newName)
	{
		if (canBeRenamed())
		{
			if (!newName.equals(getName()))
			{
				setImageDataTag(tag,newName);
			}
		}
	}	

	/**
	 * internally set the image data tags of all images contained recursively
	 * @param setTag the tag to be set
	 * @param newName the new value
	 */
	// this is currently only safe for single tags, not arrays	
	private void setImageDataTag (String setTag, String newName)
	{
		if (!tag.startsWith("$$"))
		{
			Iterator ii = getImages().iterator();
			while (ii.hasNext())
			{
				ImageDataContainer idc = (ImageDataContainer) ii.next();
				idc.setImageDataTag(setTag,newName);
				idc.save();
				refreshImage(idc);
			}

			ii = getCategories().iterator();
			while (ii.hasNext())
			{
				VisibleIndexCategory ic = (VisibleIndexCategory) ii.next(); 
				ic.setImageDataTag(setTag,newName);
			}
		}
	}	

	/* (non-Javadoc)
	 * @see pho.prowon.index.IndexCategory#canBeRenamed()
	 */
	public boolean canBeRenamed ()
	{
		return (!tag.startsWith("$$"));
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	public boolean equals(Object obj)
	{
		if (obj != null && obj instanceof VisibleIndexCategory)
		{
			if (this.tag.equals(((VisibleIndexCategory)obj).tag))
			{
				return this.toString().equals (obj.toString());
			}
		}
		return false;				
	}	

	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Object o)
	{
		int cmp = this.tag.compareTo (((VisibleIndexCategory)o).tag);
		if (cmp == 0)
		{
			// now we add a special compare logic for $$MONTH$$ because
			// we don't want the month names to be ordered alphabetically
			if (tag.equals("$$MONTH$$"))
			{
				return DateConverter.convertDateFormat(this.toString(),"MMMM","MM").compareTo(DateConverter.convertDateFormat(o.toString(),"MMMM","MM"));
			}
			return this.toString().compareTo(o.toString());
		}
		return cmp;
	}
	/* (non-Javadoc)
	 * @see pho.prowon.category.IndexCategory#exportToFolder(java.io.File)
	 */
	public void exportToFolder(File folder)
	{	
		super.exportToFolder(folder);
		if (folder.exists() && folder.isDirectory())
		{
			Iterator ii = getImages().iterator();
			while (ii.hasNext())
			{
				ImageDataContainer idc = (ImageDataContainer) ii.next();
				idc.saveAs(folder.getAbsolutePath()+File.separator+idc.getFileName());
			}
		}		
	}	
	/* (non-Javadoc)
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	public void update(Observable o, Object arg)
	{
		refreshImage ((ImageDataContainer)o);
	}
}
